/*-------------------------------------------------------------------------
 *
 * nodeResult.c
 *	  支持需要特殊代码的常量节点。
 *
 * DESCRIPTION
 *
 *		Result节点用于没有扫描关系的查询。此类查询的示例包括：
 *
 *				select 1 * 2
 *
 *				insert into emp values ('mike', 15000)
 *
 *		（请记住，在INSERT或UPDATE中，我们需要生成新行的计划树。）
 *
 *		Result节点也用于优化具有恒定
 *		资格（即，资格不依赖于扫描的数据）的查询，例如：
 *
 *				select * from emp where 2 > 1
 *
 *		在这种情况下，生成的计划是
 *
 *						Result	(具有2 > 1资格)
 *						/
 *				   SeqScan (emp.*)
 *
 *		在运行时，Result节点评估一次常量资格，EXPLAIN将其显示为一次性过滤器。如果返回
 *		为假，我们可以在不运行控制计划的情况下返回空结果集。如果返回为真，我们正常运行控制
 *		计划并返回结果。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeResult.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeResult.h"
#include "miscadmin.h"
#include "utils/memutils.h"


/* ----------------------------------------------------------------
 *		ExecResult(node)
 *
 *		返回满足资格子句的外部计划的元组。
 *		由于具有右子树的结果节点从未被计划，因此我们完全忽略右子树
 *		（暂时）.. -cim 10/7/89
 *
 *		仅包含常量子句的资格首先被检查，然后进行处理。 如果常量资格不满足，
 *		它始终返回 'nil'。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecResult(PlanState *fc_pstate)
{
	ResultState *fc_node = castNode(ResultState, fc_pstate);
	TupleTableSlot *fc_outerTupleSlot;
	PlanState  *fc_outerPlan;
	ExprContext *fc_econtext;

	CHECK_FOR_INTERRUPTS();

	fc_econtext = fc_node->ps.ps_ExprContext;

	/*
	 * 检查常量资格，例如 (2 > 1)，如果尚未完成
	 */
	if (fc_node->rs_checkqual)
	{
		bool		fc_qualResult = ExecQual(fc_node->resconstantqual, fc_econtext);

		fc_node->rs_checkqual = false;
		if (!fc_qualResult)
		{
			fc_node->rs_done = true;
			return NULL;
		}
	}

	/*
	 * 重置每个元组的内存上下文，以释放在上一个元组周期中分配的任何表达式评估
	 * 存储。
	 */
	ResetExprContext(fc_econtext);

	/*
	 * 如果 rs_done 为 true，则表示我们被要求返回一个
	 * 常量元组，并且在上次调用 ExecResult() 时已经完成，或者我们
	 * 未通过常量资格检查。 无论哪种方式，我们现在都完成了。
	 */
	if (!fc_node->rs_done)
	{
		fc_outerPlan = outerPlanState(fc_node);

		if (fc_outerPlan != NULL)
		{
			/*
			 * 从外部计划中检索元组，直到没有更多为止。
			 */
			fc_outerTupleSlot = ExecProcNode(fc_outerPlan);

			if (TupIsNull(fc_outerTupleSlot))
				return NULL;

			/*
			 * 准备计算投影表达式，这将期望
			 * 访问输入元组作为 varno OUTER。
			 */
			fc_econtext->ecxt_outertuple = fc_outerTupleSlot;
		}
		else
		{
			/*
			 * 如果我们没有外部计划，则只需从常量目标列表生成结果。 
			 * 仅执行一次。
			 */
			fc_node->rs_done = true;
		}

		/* 使用 ExecProject() 形成结果元组，并返回它 */
		return ExecProject(fc_node->ps.ps_ProjInfo);
	}

	return NULL;
}

/* ----------------------------------------------------------------
 *		ExecResultMarkPos
 * ----------------------------------------------------------------
 */
void ExecResultMarkPos(ResultState *fc_node)
{
	PlanState  *fc_outerPlan = outerPlanState(fc_node);

	if (fc_outerPlan != NULL)
		ExecMarkPos(fc_outerPlan);
	else
		elog(DEBUG2, "Result nodes do not support mark/restore");
}

/* ----------------------------------------------------------------
 *		ExecResultRestrPos
 * ----------------------------------------------------------------
 */
void ExecResultRestrPos(ResultState *fc_node)
{
	PlanState  *fc_outerPlan = outerPlanState(fc_node);

	if (fc_outerPlan != NULL)
		ExecRestrPos(fc_outerPlan);
	else
		elog(ERROR, "Result nodes do not support mark/restore");
}

/* ----------------------------------------------------------------
 *		ExecInitResult
 *
 *		为规划者产生的结果节点创建运行时状态信息
 *		并初始化外部关系
 *		（子节点）。
 * ----------------------------------------------------------------
 */
ResultState *
ExecInitResult(Result *fc_node, EState *fc_estate, int fc_eflags)
{
	ResultState *fc_resstate;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)) ||
		   outerPlan(fc_node) != NULL);

	/*
	 * 创建状态结构
	 */
	fc_resstate = makeNode(ResultState);
	fc_resstate->ps.plan = (Plan *) fc_node;
	fc_resstate->ps.state = fc_estate;
	fc_resstate->ps.ExecProcNode = fc_ExecResult;

	fc_resstate->rs_done = false;
	fc_resstate->rs_checkqual = (fc_node->resconstantqual != NULL);

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_resstate->ps);

	/*
	 * 初始化子节点
	 */
	outerPlanState(fc_resstate) = ExecInitNode(outerPlan(fc_node), fc_estate, fc_eflags);

	/*
	 * 我们不使用内部计划
	 */
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 初始化结果槽、类型和投影。
	 */
	ExecInitResultTupleSlotTL(&fc_resstate->ps, &TTSOpsVirtual);
	ExecAssignProjectionInfo(&fc_resstate->ps, NULL);

	/*
	 * 初始化子表达式
	 */
	fc_resstate->ps.qual =
		ExecInitQual(fc_node->plan.qual, (PlanState *) fc_resstate);
	fc_resstate->resconstantqual =
		ExecInitQual((List *) fc_node->resconstantqual, (PlanState *) fc_resstate);

	return fc_resstate;
}

/* ----------------------------------------------------------------
 *		ExecEndResult
 *
 *		释放通过 C 例程分配的存储
 * ----------------------------------------------------------------
 */
void ExecEndResult(ResultState *fc_node)
{
	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ps);

	/*
	 * 清空元组表
	 */
	ExecClearTuple(fc_node->ps.ps_ResultTupleSlot);

	/*
	 * 关闭子计划
	 */
	ExecEndNode(outerPlanState(fc_node));
}

void ExecReScanResult(ResultState *fc_node)
{
	fc_node->rs_done = false;
	fc_node->rs_checkqual = (fc_node->resconstantqual != NULL);

	/*
	 * 如果子节点的 chgParam 不为 null，则计划将通过
	 * 第一个 ExecProcNode 重新扫描。
	 */
	if (fc_node->ps.lefttree &&
		fc_node->ps.lefttree->chgParam == NULL)
		ExecReScan(fc_node->ps.lefttree);
}
